package com.tpshion.cloud.base.controller;

import com.github.pagehelper.PageInfo;
import com.tpshion.cloud.base.domain.dto.ConfigInfoDto;
import com.tpshion.cloud.base.domain.dto.UpdateConfigDto;
import com.tpshion.cloud.base.domain.vo.DiscoveryVo;
import com.tpshion.cloud.base.domain.vo.ServiceVo;
import com.tpshion.cloud.base.service.ConfigInfoService;
import com.tpshion.cloud.base.util.ListUtil;
import com.tpshion.cloud.common.support.Field;
import com.tpshion.cloud.common.support.Result;
import com.tpshion.cloud.core.annotation.Permission;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;

import java.util.*;
import java.util.stream.Collectors;

@RestController
public class AdminController {

    private static final Logger log = LoggerFactory.getLogger(AdminController.class);

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private ConfigInfoService configInfoService;

    @Autowired
    private RestTemplate restTemplate;

    /**
     * 获取服务列表
     * @return
     */
    @GetMapping(value = "/discovery")
    public Result<?> discoveries(){
        DiscoveryVo discoveryVo = new DiscoveryVo();
        List<String> services = discoveryClient.getServices();
        discoveryVo.setSize(services.size());
        discoveryVo.setServices(services);
        log.info("*** discoveryVo:{}",discoveryVo);
        return Result.ok(discoveryVo);
    }

    /**
     * 获取服务详情
     * @param name
     * @return
     */
    @GetMapping(value = "/service/{name}")
    public Result<?> getService(@PathVariable("name") String name){
        List<ServiceInstance> instances = discoveryClient.getInstances(name);
        return Result.ok(instances);
    }

    @PostMapping(value = "/services")
    public Result<?> getAllService(@RequestParam(value = "pageNum",defaultValue = "1") Integer pageNum,
                                   @RequestParam(value = "pageSize",defaultValue = "10") Integer pageSize,
                                   @RequestParam(value = "serviceName",required = false) String serviceName){
        List<String> services = discoveryClient.getServices();
        if(CollectionUtils.isEmpty(services)){
            return Result.ok(Collections.emptyList());
        }
        if(!StringUtils.isEmpty(serviceName)){
            List<String> collect = services.stream().filter(item -> item.contains(serviceName)).collect(Collectors.toList());
            return Result.ok(ListUtil.getPage(buildService(collect),pageNum,pageSize));
        }

        return Result.ok(ListUtil.getPage(buildService(services),pageNum,pageSize));
    }

    private List<ServiceVo> buildService(List<String> list){
        List<ServiceInstance> serviceInfos = new ArrayList<>();
        list.stream().forEach(name -> {
            List<ServiceInstance> instances = discoveryClient.getInstances(name);
            serviceInfos.addAll(instances);
        });

        if(serviceInfos.isEmpty()){
            return Collections.emptyList();
        }
        List<ServiceVo> serviceVoList = new ArrayList<>(serviceInfos.size());
        serviceInfos.stream().forEach(item ->{
            ServiceVo serviceVo = new ServiceVo();
            serviceVo.setServiceName(item.getServiceId());
            serviceVo.setHost(item.getHost());
            serviceVo.setPort(item.getPort());
            serviceVo.setSecure(item.isSecure());
            serviceVo.setUri(item.getUri());
            serviceVoList.add(serviceVo);
        });
        return serviceVoList;
    }

    /**
     * 获取访问次数
     */
    @GetMapping(value = "/reqNum")
    public Result<?> requestNum(){
        ValueOperations valueOperations = redisTemplate.opsForValue();
        int requestNum = (int) valueOperations.get(Field.REQUEST_NUM_KEY);
        log.info("requestNum:{}",requestNum);
        return Result.ok(requestNum);
    }

    /**
     * 分页查询配置信息
     * @param configInfoDto
     * @return
     */
    @PostMapping(value = "/configs")
    public Result<?> queryConfigs(@RequestBody @Validated ConfigInfoDto configInfoDto){
        log.info("*** configInfoDto:{}",configInfoDto);
        PageInfo pageInfo = configInfoService.queryConfig(configInfoDto);
        log.info("*** pageInfo:{}",pageInfo);
        return Result.ok(pageInfo);
    }

    /**
     * 保存配置信息
     */
    @PostMapping(value = "/config",params = {"action=upd"})
    @Permission("config:update")
    public Result<?> updateConfig(@RequestBody @Validated UpdateConfigDto updateConfigDto){
        log.info("*** updateConfigDto:{}",updateConfigDto);
        configInfoService.updateConfig(updateConfigDto);
        return Result.ok();
    }

    @GetMapping(value = "/trace")
    public Result<?> httpRequestTrace(@RequestParam("serviceName") String serviceName){
        String url = "http://" + serviceName + "/actuator/httptrace";
        Map<String,Object> tracesInfo = restTemplate.getForObject(url, Map.class);
        log.info("*** tracesInfo:{}",tracesInfo);
        return Result.ok(tracesInfo);
    }

    @GetMapping(value = "/jvm")
    public Result<?> JvmMemory(@RequestParam("serviceName") String serviceName){
        String totalUrl = buildGetUrl(serviceName,"/actuator/metrics/jvm.memory.max");
        String usedUrl = buildGetUrl(serviceName,"/actuator/metrics/jvm.memory.used");
        Map<String,Object> totalInfo = restTemplate.getForObject(totalUrl, Map.class);
        Map<String,Object> usedInfo = restTemplate.getForObject(usedUrl, Map.class);
        Double totalMemory = getJvmMemory(totalInfo);
        Double usedMemory = getJvmMemory(usedInfo);
        Double persent = usedMemory / totalMemory * 100;

        // 获取存活线程数
        String threadUrl = buildGetUrl(serviceName,"/actuator/metrics/jvm.threads.live");
        Map<String,Object> threadInfo = restTemplate.getForObject(threadUrl, Map.class);
        Double threadNum = getJvmMemory(threadInfo);

        // 加载的类数量
        String classLoadUrl = buildGetUrl(serviceName, "/actuator/metrics/jvm.classes.loaded");
        Map<String,Object> classLoadInfo = restTemplate.getForObject(classLoadUrl, Map.class);
        Double classLoadNum = getJvmMemory(classLoadInfo);

        // http请求数量
        String httpCountUrl = buildGetUrl(serviceName, "/actuator/metrics/http.server.requests");
        Map<String,Object> httpCountInfo = restTemplate.getForObject(httpCountUrl, Map.class);
        Double httpCount = getJvmMemory(httpCountInfo);

        Map<String,Object> result = new HashMap<>(2);
        result.put("memory", persent.intValue());
        result.put("thread", threadNum.intValue());
        result.put("classLoad", classLoadNum.intValue());
        result.put("httpCount", httpCount.intValue());
        return Result.ok(result);
    }

    private String buildGetUrl(String serviceName, String path){
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("http://")
                    .append(serviceName)
                    .append(path);
        return stringBuffer.toString();
    }

    private Double getJvmMemory(Map<String,Object> map){
        List<Map<String,Object>> measurements = (List<Map<String, Object>>) map.get("measurements");
        Map<String, Object> stringObjectMap = measurements.get(0);
        return (Double) stringObjectMap.get("value");
    }

    /**
     * 获取组件状态
     */
    @GetMapping(value = "/components")
    public Result<?> getComponents(@RequestParam("serviceName") String serviceName){
        String url = buildGetUrl(serviceName, "/actuator/health");
        Map<String,Object> info = restTemplate.getForObject(url, Map.class);
        Map<String,Object> components = (Map<String, Object>) info.get("components");
        return Result.ok(components);
    }

    /**
     * 读取日志文件
     */
    @GetMapping(value = "/logfile")
    public Result<?> getLogfile(@RequestParam("serviceName") String serviceName){
        log.info("*** getLogfile serviceName:{}",serviceName);
        String logFileUrl = buildGetUrl(serviceName, "/actuator/logfile");
        String logFile = restTemplate.getForObject(logFileUrl, String.class);
        log.info("*** logFile length:{}",logFile.length());
        return Result.ok(logFile);
    }
}
